// 高阶函数--------------------------------
// 判断类型
// @param{Object|Array|String} obj 
// return {String} 返回类型
const checkType = obj => {
    return Object.prototype.toString.call(obj);
}
// 遍历对象 数组 
// @param{Object} obj
// @param{Function} fn
const forEach = (obj, fn) => {
    let type = checkType(obj)
    switch (type) {
        case '[object Array]':
            for (let i = 0; i < obj.length; i++) {
                fn(obj[i], i)
            }
            break;
        case '[object Object]':
            for (let [key, value] of Object.entries(obj)) {
                fn(value, key)
            }
            break;
        default:
            throw new TypeError('不支持该类型遍历')
    }
}
// 验证数组的每一项是否都符合规则
// @param{Function} fn (value)=>{return value != ''}
const everyArray = (array, fn) => {
    let result = true;
    for (let value of array.values()) {
        result = fn(value)
        if (!result) { break }
    }
    return result;
}
// 验证数组的其中一项是否都符合规则
// @param{Function} fn (value)=>{return value != ''}
const someArray = (array, fn) => {
    let result = false;
    for (let value of array.values()) {
        result = fn(value)
        if (result) { break }
    }
    return result;
}
const map = (array, fn) => {
    let result = [];
    for (let [index, item] of array.entries()) {
        result.push(fn(item, index))
    }
    return result;
}
const filter = (array, fn) => {
    let result = [];
    for (let [index, item] of array.entries()) {
        fn(item) && result.push(item)
    }
    return result;
}
let unary = (fn) => {
    return fn.length == 1 ? fn : (value) => fn(value)
}
// 只运行一次函数
let once = (fn) => {
    let done = false;
    return (...arg) => {
        if (!done) {
            done = true;
            return fn.apply(this, arg);
        }
    }
}
// 缓存函数运行后的结果
let memorized = (fn) => {
    fn.cache = {}
    return (arg) => {
        return fn.cache[arg] || (fn.cache[arg] = fn(arg))
    }
}

let reduce = (array, fn) => {
    let total = array[0];
    for (let i = 1; i < array.length; i++) {
        total = fn(total, array[i])
    }
    return total;
}
// 柯里化,多参数---------------------------------------
const curry = fn => {
    return function curried(...args) {
        if (args.length < fn.length) {
            return param => {
                return curried(...[...args, param]) // arg=Array param =  1个元素
            }
        }
        return fn(...args)
    }
}
// 偏函数
// example ：
// 1. let delayLog = partial(setTimeout,undefined,10) 
// 2. delayLog(()=>{console.log(1)})
// @param{Function}fn
// @param{*} args 上个函数需要的参数
// @return{Function}
const partial = (fn, ...args) => {
    return (...fullArguments) => {
        let argIndex = 0;
        for (let i = 0; i < args.length; i++) {
            if (args[i] === undefined) {
                args[i] = fullArguments[argIndex];
                argIndex += 1;
            }
        }
        return fn(...args)
    }
}
// 组合调用 从右往做运行,将右边函数运行后的返回值 传递 给下一个函数作为参数
// example : compose(String,Math.round,item=>item*2)(3.3) "7"
// @param{Function,...}args 
// @return 最左边函数的返回值
const compose = (...args) => {
    let result = '';
    return function _compose(param) {
        if (args.length != 0) {
            result = args.pop()(param)
            return _compose(result)
        }
        return result;
    }
}
// 管道调用 从左往右运行,将左边函数运行后的返回值 传递 给下一个函数作为参数
// example compose(item=>item*2,String,)(3) "6"
// @param{Function,...}args 
// @return 最右边函数的返回值
const pipe = (...args) => {
    let result = '';
    return function _compose(param) {
        if (args.length != 0) {
            result = args.shift()(param)
            return _compose(result)
        }
        return result;
    }
}
// 链式调用调试
const tap = (param) => {
    console.log('tap:' + param)
    return param
}

// 函子 错误处理
class Maybe {
    constructor(val) {
        this.val = val;
    }
    static of(val) {
        return new Maybe(val)
    }
    isNothing() {
        return (this.val === null || this.val === undefined)
    }
    map(fn) {
        return this.isNothing() ? Maybe.of(null) : Maybe.of(fn(this.val))
    }
    join() {
        return this.isNothing() ? Maybe.of(null) : this.val
    }
    chain(fn) {
        return this.map(fn).join()
    }
}
class Either {
    constructor(val = null, error = null) {
        this.val = val;
        this.error = error
    }
    static of(val, error) {
        return new Either(val, error)
    }
    map(fn) {
        let val = null;
        let error = null;
        try {
            val = fn(this.val)
        } catch (e) {
            error = e.toString()
        }
        return Either.of(val, error);
    }
}